package dk.flashnotes.fff {
	import dk.flashnotes.fff.contextMenu.ContextMenuNavigation;	
	import dk.flashnotes.fff.googleAnalytics.GABridge;	import dk.flashnotes.fff.googleAnalytics.GABridgeEvent;	import dk.flashnotes.fff.helpers.HelpersControl;	import dk.flashnotes.fff.helpers.dummyMenu.DummyMenu;	import dk.flashnotes.fff.helpers.pageInspector.PageInspector;	import dk.flashnotes.fff.page.PageControl;	import dk.flashnotes.fff.page.PageData;	import dk.flashnotes.fff.tree.TreeControl;	import dk.flashnotes.fff.tree.TreeControlEvent;	import dk.flashnotes.fff.tree.TreeNode;	import dk.flashnotes.fff.utils.DescribeTypeClasseWrap;		import org.osflash.thunderbolt.Logger;		import flash.display.DisplayObjectContainer;	import flash.display.Sprite;	import flash.display.Stage;	import flash.events.Event;	import flash.events.EventDispatcher;	import flash.net.URLLoader;	import flash.net.URLRequest;	
	/**
	 * @author Josef Kj�rgaard
	 */
	public class FFFBase extends EventDispatcher {

		private static var _oInstance : FFFBase;
		private static var _bCreatingInstance : Boolean = false;

		private var _oElementFactory : ElementFactory;
		private var _xmlTree : XML;
		private var _oDataLoader : URLLoader;
		private var _oTreeControl : TreeControl;
		private var _xmlFullData : XML;

		private var sprPageContainer : Sprite;
		private var _cPartClassRefrence : Class;
		private var _a_cAPartTypes : Array = new Array();
		private var _a_cAPageType : Array = new Array();

		private var _cPageClassRefrence : Class;
		private var _oPageControl : PageControl;
		private var _doDummyTree : DummyMenu;
		private var _bTreeControlStarted : Boolean = false;
		private var _xmlData : XML;
		private var _xmlPages : XML;
		private var _oHelpersControl : HelpersControl;
		private var _doHelperContainer : Sprite;
		
		//settings prop
		private var _bHelpersEnabled : Boolean=true;
		private var _bUseSwfAddress : Boolean;
		private var _bTracingEnabled : Boolean = false;
		private var _bRemberBlockedNodes : Boolean = true;
		private var _oGABridge : GABridge;
		private var _sprGAHolder : Sprite;
		private var _oContextMenuNavigation : ContextMenuNavigation;

		public function FFFBase() {
			if( !_bCreatingInstance ) throw new Error("Singleton and can only be accessed through FFFBase.get_oInstance()");
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// inits
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		
		public function initFFF(doRootForFFF : Sprite, bUseSwfAddress : Boolean = true, xmlTree : XML = null, oCustomElementFactory : ElementFactory = null) : void {
			
			setupContainers(doRootForFFF);
			
			_bUseSwfAddress = bUseSwfAddress;
			
			if (oCustomElementFactory) {
				_oElementFactory = oCustomElementFactory;
			} else {
				_oElementFactory = new ElementFactory();
			}
			registerData(xmlTree);		}


		/**
		 * The main initiation method. Handles loading and subsequently the node tree, TreeControl, PageControl..
		 * Pass a custom ElementFactory to sypply system with extended elements of TreeNode, APage and APart.
		 * The actions taken by this method will dispatch FFFBaseEvent.DATA_LOADED and FFFBaseEvent.etTREE_CREATED
		 * 
		 * Note: SWFAddress will only work if the .js is supplied in the embedding code. 
		 * 
		 * @param sprRootForFFF			Sprite			The DisplayObjectContainer for FFFBase to place APages and AParts in.
		 * @param bUseSwfAddress		Boolean			Wheter to use SWFAddress or not.
		 * @param sXMLTreeURL			String			The url to the main xml.
		 * @param oCustomElementFactory	ElementFactory	Option to pass custom ElementFactory.
		 */
		public function loadAndInitFFFTree(sprRootForFFF : Sprite, bUseSwfAddress : Boolean, sXMLTreeURL : String, oCustomElementFactory : ElementFactory = null) : void {
			
			setupContainers(sprRootForFFF);
			
			_bUseSwfAddress = bUseSwfAddress;
			
			if (oCustomElementFactory) {
				_oElementFactory = oCustomElementFactory;
			} else {
				_oElementFactory = new ElementFactory();
			}
			
			// init load
			_oDataLoader = new URLLoader();
			_oDataLoader.addEventListener(Event.COMPLETE, onXMLTreeLoaded, false, 0, true);
			_oDataLoader.load(new URLRequest(sXMLTreeURL));	
		}

		private function setupContainers(sprRootForFFF : Sprite) : void {
			sprPageContainer = new Sprite();
			sprRootForFFF.addChild(sprPageContainer);
			
			_doHelperContainer = new Sprite();
			sprRootForFFF.addChild(_doHelperContainer);
		}

		private function onXMLTreeLoaded(e : Event) : void {
			registerData(XML(_oDataLoader.data));
		}
		
		private function registerData(xmlData : XML) : void {
			
			_xmlFullData = xmlData;

			//
			dispatchEvent(new FFFBaseEvent(FFFBaseEvent.etDATA_LOADED));
			
			// tree data
			_xmlTree = new XML(_xmlFullData[EMenuXMLKeys.TREE]);
			
			// data xml
			if (_xmlFullData[EMenuXMLKeys.DATA] != undefined) {
				_xmlData = XML(_xmlFullData[EMenuXMLKeys.DATA]);
				
				if (_xmlData[EMenuXMLKeys.PAGES] != undefined) {
					_xmlPages = XML(_xmlData[EMenuXMLKeys.PAGES]);
				}
			}
			// pages xml
			initTree();
		}
		
		public function get_oPageDataByRefrenceId(sRefId : String) : PageData {
			
			for each (var xmlPage : XML in _xmlPages.page) {
				var sRefIDXML : String = xmlPage.@[EMenuXMLKeys.REF_ID];
				if (sRefId == sRefIDXML) {
					return new PageData(xmlPage);
				}
			}
			return null;
		}
		

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// google analytics setup
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		/**
		 * Load and initialize the Google Analytics API
		 * This will load the external swf making the GA API avalible. 
		 * 
		 * @param sGAModulePath		String			The path for the external files: 'base_config.xml' and 'fff_ga_module.swf'.
		 * @param sConfigID			String			The config id is used to refrence different config modes in the 'base_config.xml'.
		 * @param doGAHolder		Sprite			The Sprite is used to refrence the stage. Used by the GA debug tool.
		 */
		public function loadAndInitGoogleAnalyticsModule(sGAModulePath : String, sConfigID : String, sprGAHolder : Sprite=null) : void {
			
			_sprGAHolder = sprGAHolder;
			
			if (_sprGAHolder == null) {
				_sprGAHolder = new Sprite();
				_doHelperContainer.addChild(_sprGAHolder);
			}
			
			_oGABridge = new GABridge();
			_oGABridge.addEventListener(GABridgeEvent.LOADED_AND_READY, onGAReady, false, 0, true);
			_oGABridge.loadAndInitialize(sGAModulePath, sConfigID, _sprGAHolder);
		}

		private function onGAReady(event : GABridgeEvent) : void {
			dispatchEvent(new FFFBaseEvent(FFFBaseEvent.GOOGLE_ANALYTICS_BRIDGE_READY));
			setupEventListenint();
		}				private function setupEventListenint() : void {
			_oTreeControl.addEventListener(TreeControlEvent.etNEW_TREE_NODE_SELECTED, onNewNodeSelected, false, 0, true);
		}
		
		private function onNewNodeSelected(event : TreeControlEvent) : void {
			_oGABridge.trackPageview(_oTreeControl.oTNCurrent.oTNData.sUrl);
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// tree setup
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function initTree() : void {
			
			_oPageControl = new PageControl();
			_oTreeControl = new TreeControl();
			_oTreeControl.initTreeByXML(_xmlTree, _bUseSwfAddress);
			dispatchEvent(new FFFBaseEvent(FFFBaseEvent.etTREE_CREATED));
			
			setupHelpers();
			setupContextMenuNavigation();
		}


		/**
		 * Starts the tree control.
		 * 
		 * @see TreeControl
		 */
		public function startTreeControl() : void {
			_bTreeControlStarted = true;
			_oTreeControl.startTreeControl();
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// contextmenu handling
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		private function setupContextMenuNavigation() : void {
			_oContextMenuNavigation = new ContextMenuNavigation();
		}

		
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// element class ref lib
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public function get_cPartTypeFromPartTypeAlias(sPartTypeAlias : String) : Class {
			
			if (_cPartClassRefrence) {
				if (_cPartClassRefrence[sPartTypeAlias]) {
					return _cPartClassRefrence[sPartTypeAlias] as Class;
				}
			} 
			for each (var cPartType : Class in _a_cAPartTypes) {
				var oDTC : DescribeTypeClasseWrap = new DescribeTypeClasseWrap(cPartType);
				if (oDTC .sClass == sPartTypeAlias) {
					return cPartType;
				}
			}
			if (_bTracingEnabled){
				Logger.warn("No PartClass refrence found for : " + sPartTypeAlias + ". Creating DefaultPart");
			}
			return _oElementFactory.get_cDefaultPartType();
		}

		public function get_cPageClassRefrenceFromAlias(sClassAlias : String) : Class {
			
			if (_cPageClassRefrence) {
				if (_cPageClassRefrence[sClassAlias]) {
					return _cPageClassRefrence[sClassAlias] as Class;
				}
			}
			for each (var cPageType : Class in _a_cAPageType) {
				var oDTC : DescribeTypeClasseWrap = new DescribeTypeClasseWrap(cPageType);
				if (oDTC .sClass == sClassAlias) {
					return cPageType;
				}
			}
			if (_bTracingEnabled) {
				Logger.warn("No PageClass refrence found for : " + sClassAlias + ". Creating DefaultPage");
			}
			return _oElementFactory.get_cDefaultPageType();
		}
		
		/**
		 * Register APart types. This is where you register the APart subclasses that needs to be avalible for
		 * autogeneration by APages.
		 * 
		 * @param args			APart			Takes a collection of APart subclasses.
		 */
		public function register_cAPartTypes(... args) : void {
			for each (var cType : Class in args) {
				_a_cAPartTypes.push(cType);
			}
		}
		
		/**
		 * Register APage types. This is where you register the APage subclasses that needs to be avalible for
		 * autogeneration by PageControl.
		 * 
		 * @param Takes a collection of APart subclasses.
		 */
		
		public function  register_cAPageTypes(... args) : void {
			for each (var cType : Class in args) {
				_a_cAPageType.push(cType);
			}
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// setup helpers
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function setupHelpers() : void {
			
			if (FFFBase.get_oInstance().bTracingEnabled) {
				Logger.info("setup helpers");
			}
			
			if (_bHelpersEnabled) {
				_oHelpersControl = new HelpersControl(_doHelperContainer);
				
				_oHelpersControl.addHelper(new DummyMenu(_oTreeControl));
				_oHelpersControl.addHelper(new PageInspector());
			}
			
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// singleton stuff
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public static function get_oInstance() : FFFBase {
			if (!_oInstance) {
				_bCreatingInstance = true;
				_oInstance = new FFFBase();
				_bCreatingInstance = false;
			}
			return _oInstance;
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// getter setters
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public function get bTracingEnabled() : Boolean {
			return _bTracingEnabled;		}
		public function set bTracingEnabled(bTracingEnabled : Boolean) : void {
			_bTracingEnabled = bTracingEnabled;		}

		public function get oElementFactory() : ElementFactory {
			return _oElementFactory;
		}

		public function get doPageContainer() : DisplayObjectContainer {
			return sprPageContainer;
		}

		public function get oPageControl() : PageControl {
			return _oPageControl;
		}

		public function get doDummyTree() : DummyMenu {
			return _doDummyTree;
		}

		public function get bTreeControlStarted() : Boolean {
			return _bTreeControlStarted;
		}

		public function get oTreeControl() : TreeControl {
			return _oTreeControl;
		}

		public function get oStageRef() : Stage {
			return sprPageContainer.stage;
		}

		public function get oRootNode() : TreeNode { 
			return _oTreeControl.oRootNode; 
		}

		public function get xmlFullData() : XML {
			return _xmlFullData;
		}
		
		public function get bHelpersEnabled() : Boolean { 
			return _bHelpersEnabled; 
		}
		public function set bHelpersEnabled(oNewValue:Boolean) : void { 
			_bHelpersEnabled=oNewValue;
		}
		
		public function get bRemberBlockedNodes() : Boolean { 
			return _bRemberBlockedNodes; 
		}
		
		public function set bRemberBlockedNodes(oNewValue:Boolean) : void { 
			_bRemberBlockedNodes=oNewValue;
		}
	}
}
